关于R语言`if...else...return`的一点思考

一直以来对于return()函数很困惑。在代码到低是写一个return()比较好,还是写多个return比较好呢?纠结的很。今天偶然间在网上看到一个问题:一个函数里面用一个return 好 还是多个return 好?,感觉有点明白了。在上一篇博客解析fivenum函数的源码中,fivenum函数也多次“用到”return,当然只是隐式的调用return函数。那么问题来了?我到底该用哪一种呢???

我自己写了一个函数,然后用microbenchmark包输出两个函数(各执行10000次)的执行时间,比较哪个更好,以后就用哪个了。^_^

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ff1 <- function(x, y) {
if (x < y) {
return(x + y)
}
if (x == y) {
return(x - y)
}
if (x > y) {
return(x * y)
}
}
ff2 <- function(x, y) {
if (x < y) {
z <- x + y
}
if (x == y) {
z <- x - y
}
if (x > y) {
z <- x * y
}
return(z)
}
1
ff1(10, 4)
1
#> [1] 40
1
ff2(10, 4)
1
#> [1] 40

载入microbenchmark

1
2
3
4
library(microbenchmark)
microbenchmark(ff1(10, 4), ff2(10, 4),
times = 10000, # 执行10000次
unit = "ns") # 时间为纳秒
1
2
3
4
#> Unit: nanoseconds
#> expr min lq mean median uq max neval cld
#> ff1(10, 4) 0 446 1428.514 447 892 7953122 10000 a
#> ff2(10, 4) 0 446 1643.996 447 893 9130717 10000 a

结论:两个函数在执行速度上并没有太大的差别。但是就简洁性而言,第一种多个return()更好。

下面来再来看看if...else语句的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fg1 <- function(x, y) {
if (x < y) {
return(x * y)
}
return(x + y)
}
fg2 <- function(x, y) {
if (x < y) {
return(x * y)
} else {
return(x + y)
}
}
1
fg1(3, 5)
1
#> [1] 15
1
fg1(10, 8)
1
#> [1] 18
1
fg2(3, 5)
1
#> [1] 15
1
fg2(10, 8)
1
#> [1] 18

下面看看这两个函数的性能差异

1
2
3
4
library(microbenchmark)
microbenchmark(fg1(10, 4), fg2(10, 4),
times = 10000,
unit = "ns")
1
2
3
4
#> Unit: nanoseconds
#> expr min lq mean median uq max neval cld
#> fg1(10, 4) 0 0 349.8470 446 447 23651 10000 a
#> fg2(10, 4) 0 0 349.3135 446 447 25881 10000 a

结论:上面的两个函数性能差异也不是太大。但是就就简洁性而言,没有else的版本更好。其实,这里的else根本就是多余的。因为一旦if条件的结果为真,程序会直接返回return()中的值。后面的代码不回被执行。只不过加了else代码更加清晰一些。

当然,下面的这种情况加else更好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
f1 <- function(x, y) {
if (x < y) {
z <- x - y
}
if (x == y) {
z <- x + y
}
if (x > y) {
z <- x * y
}
return(z)
}
f2 <- function(x, y) {
if (x < y) {
z <- x - y
} else if (x == y) {
z <- x + y
} else if (x > y) {
z <- x * y
}
return(z)
}
1
f1(3, 6)
1
#> [1] -3
1
f2(3, 6)
1
#> [1] -3
1
2
3
4
library(microbenchmark)
microbenchmark(f1(3, 6), f2(3, 6),
times = 10000,
unit = "ns")
1
2
3
4
#> Unit: nanoseconds
#> expr min lq mean median uq max neval cld
#> f1(3, 6) 0 446 1440.03 447 447 9028085 10000 a
#> f2(3, 6) 0 446 1212.27 446 447 7865215 10000 a

结论:下面的加else的函数明显快于上面的没有加else的函数。

这是因为这里的判定条件为3 < 6,显然这个为真,但是第一个会接着执行下面的判断函数,直到把所有的判定条件全部执行完毕为止。最终返回为真的结果。但是第二个函数就不一样了,因为后面出现了else语句,编译器一看到这个else,就知道后面的都不用执行了。所以第二个函数要快一些。

写到这里,心里终于有数了。在函数中,就用多个return吧,当然退而求其次,也可以选择if...else...return